home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / Developer Documentation / Recipes, Tech Notes & Articles / Recipes / Data Interchange / Drag and Drop Recipes < prev    next >
Encoding:
Text File  |  1996-04-19  |  24.4 KB  |  529 lines  |  [TEXT/ttxt]

  1. OpenDoc™ Recipes
  2.  
  3.  
  4. Drag-and-Drop Recipes
  5. By The OpenDoc Design Team
  6. April 18, 1996
  7.  
  8. © 1993-1996  Apple Computer, Inc. All Rights Reserved.
  9. Apple, the Apple logo, AppleScript, Bento, Macintosh, QuickTime, and OpenDoc are 
  10. registered trademarks of Apple Computer, Inc.
  11. Finder, Mac, and QuickDraw are trademarks of Apple Computer, Inc. 
  12. SOM, SOMObjects, and System Object Model are licensed trademarks of IBM Corporation. 
  13.  
  14. Changes since DR4
  15.  
  16. 1) Added discussion of the clone kind to use when performing a drop.
  17.  
  18. Changes since DR3
  19.  
  20. 1) Added discussion of an embedded frame's in-limbo flag.
  21.  
  22. Changes since DR2
  23.  
  24. 1) Added kODPropMouseDownOffset.
  25. 2) Fixed refcounting method names.
  26. 3) Fixed parameter passing errors with SetValue and GetValue.
  27.  
  28. About Drag-and-Drop Recipes 
  29.  
  30. The Drag and Drop protocol provides a direct manipulation alternative to the clipboard for copying and moving data between parts in a document, between documents and between documents and the desktop. 
  31.  
  32. This document contains a number of recipes for transferring data through the OpenDoc Drag-and-Drop mechanism.  Refer to the Class Documentation for a description of individual methods.
  33.  
  34. The examples use exception handling in the form of TRY…CATCH_ALL…ENDTRY blocks to catch exceptions, and assumes that errors returned by SOM methods are automatically thrown upon return.
  35.  
  36. WARNING: These recipes are not actual code, and may contain errors. They serve as examples of how to use the OpenDoc APIs to  accomplish specific objectives in particular circumstances. Actual part code usually needs to draw from several recipes to handle the variety of conditions that occur in a real application.
  37.  
  38. The Drag-and-Drop Focus (or the lack of it)
  39.  
  40. There is no Drag-and-Drop focus because only one part can be initiating a drop at a time and the drag is synchronous.
  41.  
  42. Drag and Drop Clone Kinds
  43.  
  44. When performing a drop, a part must use the clone kind kODCloneDropCopy or kODCloneDropMove in its call to BeginClone.  It should not use the clipboard clone kind kODClonePaste.  The drag attributes determine which clone kind to use; see the example below for details.
  45.  
  46. Undo of a Drag-and-Drop
  47.  
  48. A copy or move using Drag-and-Drop should be undoable.  An undo transaction should be started by the part initiating the drag; the part receiving the drop may add undo actions to the transaction.  The transaction should be ended after the drop completed by the part initiating the drag.  See the Undo Recipes document in the Edit Menu folder for details.
  49.  
  50. Embedded Frames and their In-Limbo flag
  51.  
  52. When an embedded frame is dragged or dropped, parts must set the in-limbo flag  of the frame correctly.  Parts are also responsible for setting the in-limbo flag  when a drag and drop operation is undone, redone, or commited via their DisposeActionState method.   The code examples here demonstrate the proper manipulation of this flag during the drag and drop, but do not cover how your part must support the in-limbo flag in your UndoAction, RedoAction, or DisposeActionState methods.  The complete recipes for setting this flag, and for the actions to take in DisposeActionState, are discussed in the OpenDoc Programmer's Guide.
  53.  
  54.  
  55. Recipes
  56.  
  57.  
  58. Preparing for a Drop:
  59.  
  60. A part which can receive a drop should call IsDroppable on all its display frames which can accept a drop. This can be done during ODPart::DisplayFrameAdded or ODPart::DisplayFrameConnected.
  61.  
  62. SOM_Scope void  SOMLINK MyPartDisplayFrameAdded(AppleTest_Container *somSelf, Environment *ev,
  63.   ODFrame* frame)
  64. {
  65.  ......
  66.  frame->SetDroppable(ev, kODTrue);
  67.  ......
  68. }
  69.  
  70. If your part does not call SetDroppable on a display frame, the part will not receive any DragEnter, DragWithin, DragLeave and Drop from any facet on that frame.
  71.  
  72. If your part no longer wishes to receive drops from a particular frame, it can call ODFrame::SetDroppable with kODFalse.
  73.  
  74.  
  75. Detecting a Drag (Handling Mouse-Down Events):
  76.  
  77. Note that a drag can be initiated by any part even when the part does not have any display frame that is set to receive drops.
  78.  
  79. A drag is initiated by a mouse-down event — kODEvtMouseDown or kODEvtMouseDownEmbedded — received by your HandleEvent method. (For more information on handling events, see the Basic Event Handling recipe.) When handling a mouse-down event, check to see if the coordinates of the mouse-down in the facet are within an item that can be dragged, such as a content item, a text selection, or a selected or bundled embedded frame.
  80.  
  81. If you’ve determined that the mouse-down is on something draggable, you need to wait and see if the user actually starts dragging, or just releases the mouse without moving it (a click.) On the Macintosh, call the Drag Manager’s WaitMouseMoved routine. This takes no parameters and returns a value of kODTrue if the mouse has moved more than two pixels and a drag is about to begin, or kODFalse if the user released the mouse button without moving the mouse more than two pixels. In the latter case, you should treat the event as just a regular click.
  82.  
  83. The above discussion is actually slightly simplified: this is what applies in the usual case where the facet is in an active window. Handling a mouse-down in an inactive window requires a bit more work. The OpenDoc Human Interface Guidelines state that a click in an inactive window should just activate the window; the user does not “click through” to the content. However, the user can drag an item from an inactive window without activating the window. That last bit is important: if the window activated, it might cover up the very place in another window that you wanted to drag the item to. (Those who used a Mac before System 7 will recall how much fun this was.)
  84.  
  85. On the Mac this breaks down into two cases, one where the entire document (process, layer) is inactive, and one where it’s active but the particular window is inactive. These cases look the same to the user, but the Macintosh’s layer model makes them different to implement.
  86.  
  87. If the document’s process/layer is active, you receive a normal kODEvtMouseDown or kODEvtMouseDownEmbedded event. The logic to handle these looks like this:
  88.  
  89. if the mouse is over a draggable item and WaitMouseMoved() returns true
  90.  
  91.  Initiate a drag (see Initiating a drag)
  92.  
  93. else if the window is active
  94.  
  95.  handle the click normally (perhaps starting a drag-selection)
  96.  
  97. else
  98.  
  99.  activate the window
  100.  
  101. return kODTrue
  102.  
  103. In the case where the document’s process/layer is inactive, you receive a different event, either kODEvtBGMouseDown or kODEvtBGMouseDownEmbedded. These have to be handled somewhat differently:
  104.  
  105. if the mouse is over a draggable item and WaitMouseMoved() returns true
  106.  
  107.  Initiate a drag (see Initiating a drag)
  108.  
  109.  return kODTrue;
  110.  
  111. else
  112.  
  113.  return kODFalse;
  114.  
  115. Note that no attempt is made to handle anything other than a drag (in particular the window is not activated) and that you have to return kODFalse if there was no drag. This is so that OpenDoc can tell the Process Manager whether or not the document process needs to be activated after your handler returns.
  116.  
  117. Warning: It’s important not to do anything but initiate a drag when handling a BGMouseDown event. At the time you’re sent the event, the process is in a Process Manager callback similar to a drag handler, and the same rules apply: interacting with the user by putting up a dialog, or switching processes, will hang or crash the system.
  118.  
  119.  
  120. Initiating a drag:
  121.  
  122. Once a part knows that a drag is to be initiated, the source part should call ODSession's GetDragAndDrop to get the ODDragAndDrop object. Then it should call ODDragAndDrop's Clear and ODDragAndDrop's GetContentStorageUnit to get the ODStorageUnit to where data of the dragged object(s) is copied. Then it can initiate drag by calling ODDragAndDrop's StartDrag. An image for dragging feedback is provided by the source part.
  123.  
  124. Drag Outline:
  125.  
  126. On the Macintosh, the imageType is kODDragImageRegionHandle and the image data is a byte array containing the handle (not the content of the handle) of the drag region in global coordinates. It is the part's responsiblity to build this drag region.
  127.  
  128. Ref con for StartDrag:
  129.  
  130. The ODEventData pointer is passed in as the refCon. This information is important for interoperating with the Macintosh Drag Manager.
  131.  
  132. Dragging a frame:
  133.  
  134. In general, it is a bad idea to drag a frame into itself. Therefore, if you are dragging a frame or a set of frames, you should call ODFrame::SetDragging using kODTrue. This will tell OpenDoc Drag-and-Drop that these frames are not supposed to accept this drop.
  135.  
  136. Mouse down offset:
  137.  
  138. The source part should write out the mouse down offset from the top-left corner of the selectuib. This enables the destination part to place the selection at the correct offset from the mouse up position when it is dropped.
  139.  
  140. Recipe:
  141.  
  142. // Get the ODDragAndDrop object from the session.
  143.  
  144. ODDragAndDrop* dragAndDrop = 
  145.      somSelf->GetStorageUnit(ev)->GetSession(ev)->GetDragAndDrop(ev);
  146.  
  147. // Reinitialize the ODDragAndDrop object.
  148.  
  149. dragAndDrop->Clear(ev);
  150.  
  151. // Get the Storage Unit where data for dragged objects are going to be written.
  152.  
  153. ODStorageUnit* storageUnit = dragAndDrop->GetContentStorageUnit(ev);
  154.  
  155. // Write out the data.
  156. // Please refer to the following sections in Clipboard recipes:
  157. // Annotating the clipboard with a frame shape
  158. // Writing a frame shape to the clipboard
  159. // Always use BeginClone and EndClone
  160. // Putting intrinsic content on the clipboard
  161. // Copying content to the clipboard
  162. // Putting a single embedded frame on the clipboard.
  163. .......
  164.  
  165. // If you are dragging a frame or a set of frames, you should notify OpenDoc Drag-and-Drop
  166. // that this frame is being dragged.
  167.  
  168. frame->SetDragging(ev, kODTrue);
  169.  
  170. // Anticipate a move by marking all dragged frames as in-limbo
  171. frame->SetInLimbo(ev, kODTrue);
  172.  
  173. // Write out the mouse down offset.
  174. storageUnit->AddProperty(ev, kODPropMouseDownOffset);
  175. storageUnit->AddValue(ev, kODPoint);
  176.  
  177. // Calculate offset
  178. ...
  179. // Set up byte array
  180. ...
  181. // Write out the mouse down offset.
  182. storageUnit->SetValue(ev, &ba);
  183.  
  184. // Create byte arrays for dragRn and refCon (which is the event record)
  185.  
  186. ODByteArray dragRgnBA;
  187. dragRgnBA._length = sizeof(RgnHandle);
  188. dragRgnBA._maximum = sizeof(RgnHandle);
  189. dragRgnBA._buffer = &dragRgn;
  190.  
  191. ODByteArray eventBA;
  192. eventBA._length = sizeof(ODEventData*);
  193. eventBA._maximum = sizeof(ODEventData*);
  194. eventBA._buffer = &event;        // event is of type ODEventData*.
  195.  
  196. // Initiate the drag
  197.  
  198. ODPart* destPart;
  199.  
  200. dropResult = 
  201.  dragAndDrop->StartDrag(sourceFrame, kODDragImageRegionHandle, &dragRgnBA, &destPart, &eventBA);
  202.  
  203. // If you have set any frame to not accept any drop, unset it.
  204.  
  205. frame->SetDragging(ev, kODFalse);
  206.  
  207. // If the drag was not a move, dragged frames are no longer in limbo
  208. if ( dropResult != kODDropMove )
  209. {
  210.   frame->SetInLimbo(ev, kODFalse);
  211. }
  212.  
  213. if ( dropResult == kODDropMove )
  214. {
  215.   // If a frame or set of frames was moved, old facets must be removed.
  216.   // The source part must take into account that a moved frame may now be embedded in another part,
  217.   // and may have new facets in its new containing frame.
  218.   // The source part cannot use the moved frame's facet iterator.
  219.   // It must instead use the containing facet's iterator to identify the facets to be deleted.
  220.   ...
  221. }
  222.  
  223. Tracking a drag:
  224.  
  225. Entering a Part’s facet:
  226.  
  227. ODPart's DragEnter is called when the mouse enters a facet. The part should examine the available data types of the dragged items using the ODDragItemIterator passed in. If the part can handle a drop of the dragged object, it should provide the appropriate feedback (e.g., adorning the droppable frame, changing the cursor). If the destination part cannot handle the data types, nothing should be done.
  228.  
  229. If there is more than one drag item, the Part should make sure that it can accept all the drag items. If there is one or more drag item that the Part cannot accept, it should return kODFalse from DragEnter and do no visual feedback.
  230.  
  231. The following code fragment shows a simple example where a part which can only accepts one kind (kMyKind) determines whether the current drag can be accepted.
  232.  
  233. ODBoolean CanAcceptThisDrop(Environment* ev, ODDragItemIterator* dragInfo)
  234. {
  235.  // assuming that we can accept all the drag items
  236.  ODBoolean canAccept = kODTrue;
  237.  for (ODStorageUnit* dragSU = dragInfo->First(ev);
  238.    (dragInfo->IsNotComplete(ev) && (canAccept == kODTrue));
  239.    dragSU = dragInfo->Next(ev))
  240.  {
  241.   if (dragSU->Exists(ev, kODPropContents, kMyKind, 0) == kODFalse)
  242.    canAccept = kODFalse;
  243.  }
  244.  return canAccept;
  245. }
  246.  
  247. However, checking the kinds is not enough to provide the full feedback according to the Macintosh Drag Manager guidelines. The guidelines dictate that no highlighting should be shown on the frame from which the drag is initiated until the drag has left and returned to the frame. Finder (for system 7.5) provides an excellent example. When you drag a file from the Finder, the window in which the file resides is not highlighted. However, when you drag the file out of the window and back to the window, highlighting is shown.
  248.  
  249. In order to help part developers implement this, OpenDoc Drag-and-Drop provides two Drag Attributes:
  250.  
  251. kODDragIsInSourceFrame shows that the drag still hasn't left the source frame. This can be used in place of dragHasLeftSenderWindow of the Drag Manager which can only handle applications.
  252. kODDragIsInSourcePart shows that the drag still hasn't left the source part. There is no Macintosh Drag Manager equivalent of this status code.
  253.  
  254. ODDragResult MyPartDragEnter(MyPart* somSelf,
  255.       Environment* ev,
  256.       ODDragItemIterator* dragInfo,
  257.       ODFacet* facet,
  258.       ODPoint where)
  259. {
  260.  
  261.  // Get the Drag-and-Drop object associated with this drag.
  262.  // Note that the the session from dropSU may not be the same as the session of the
  263.  // target part.
  264.  ODDragAndDrop* dad = dropSU->GetSession(ev)->GetDragAndDrop(ev);
  265.  ODULong dragAttributes = dad->GetDragAttributes(ev);
  266.  
  267.  // Determine whether we can handle this drag.
  268.  ODBoolean canAccept = CanAcceptThisDrag(ev, dragInfo);
  269.  
  270.  // If we can accept the data and the drag has left the source frame, 
  271.  // do some adornment.
  272.  // Here we are going to put up drag hilite on the frame itself.
  273.  
  274.  if ((canAccept == kODTrue) && !(dragAttributes & kODDragIsInSourceFrame)) {
  275.   // Use FocusLib to set up the grafport.
  276.   // Since this is a stack variable, it will be cleaned up when we exit.
  277.   CFocus prepareForRendering(ev, facet);
  278.  
  279.   // Get the platform frame shape.
  280.   ODFrame* displayFrame = facet->GetFrame(ev);
  281.   ODShape* frameShape = displayFrame->AcquireFrameShape(ev, kODNULL);
  282.   RgnHandle bRgn = frameShape->GetQDRegion(ev);
  283.   frameShape->Release(ev);
  284.  
  285.   // Use the Macintosh Drag Manager to show the hilite.
  286.   // Note that currently the drag hilite drawn by Macintosh Drag Manager only shows up
  287.   // on a white background. If you have any other background color, the drag hilite will
  288.   // not be shown.
  289.   // You can also provide your own hiliting without going through the Drag Manager.
  290.  
  291.   // Use the Drag Reference to call the Macintoh Drag Manager.
  292.   ShowDragHilite(dad->GetDragReference(ev), bRgn, true);
  293.  }
  294.  
  295.  // Return ODDragResult to show whether a Drop can happen in this facet.
  296.  return canAccept;
  297. }
  298.  
  299.  
  300. Within a Part’s facet:
  301.  
  302. ODPart's DragWithin is called continuously when the mouse is still in the facet. This allows the part to do any processing desired. One good example is when the frame has several hot spots where objects can be dropped. If the mouse is not over these hot spots, the cursor may need to be changed to reflect that the no dropping can be done there even though it is still in a droppable frame. Again, a ODDragItemIterator is passed in so that the part can examine the availabe data types of the dragged objects.
  303.  
  304. ODPart's DragWithin also provides a chance for the part to examine the state of the machine. For example, some part may want to find out whether the modifier keys are down or not.
  305.  
  306.  
  307. Leaving a Part’s facet:
  308.  
  309. ODPart's DragLeave is called when the mouse leaves a droppable frame. This allows the part to clean up after a drag within it (e.g., removing adornment on the frame, changing the cursor back to its original form).
  310.  
  311.  
  312. Receiving a Drop:
  313.  
  314. If the mouse is released within a facet, ODPart's Drop is called on the part which owns the facet. The part can then figure out whether it can receive the dragged object using the ODDragItemIterator passed in.
  315.  
  316. OpenDoc Human Interface Specification defines guidelines for the destination part to decide whether a drop should be a move or a copy. OpenDoc Drag-and-Drop provides this information to the part through the OpenDoc Drag Attributes (not to be confused with the attributes from the Macintosh Drag Manager). The part can get the OpenDoc Drag Attributes through the ODDragAndDrop object. When ODDragAndDrop::GetDragAttributes is called, a ODULong is returned. The value of the OpenDoc Drag Attributes reflects important information about the Drop:
  317.  
  318. kODDropIsInSourceFrame: The drop happens in the frame where the drag is initiated.
  319. kODDropIsInSourcePart: The drop happens in the part when the drag is initiated.
  320. kODDropIsMove: The drop is a move.
  321. kODDropIsCopy: The drop is a copy.
  322. kODDropIsPasteAs: The destination part should put up the Paste As Dialog and enable the user to take further actions.
  323.  
  324. The destination part should look for kODPropMouseDownOffset to position the dropped data according to the original mousedown offset.
  325.  
  326. ODDropResult MyPartDrop(MyPart* somSelf,
  327.               Environment* ev,
  328.               ODDragItemIterator *dropInfo,
  329.               ODFacet* facet,
  330.               ODPoint where)
  331. {
  332.  ......
  333.  
  334.  // Determine whether we can handle this drag.
  335.  ODBoolean canAccept = CanAcceptThisDrag(ev, dragInfo);
  336.  
  337.  // Get the OpenDoc Drop Attirbutes for this drop
  338.  ODDragAndDrop* dad = somSelf->GetStorageUnit(ev)->GetSession(ev)->GetDragAndDrop(ev);
  339.  ODULong dragAttributes = dad->GetDragAttributes(ev);
  340.  
  341.  ODDropResult dropResult = kODDropFail;
  342.  
  343.  if (dragAttributes & kODDropIsMove) {
  344.   // This is a move
  345.   // Refer to the following Clipboard recipes:
  346.   // Incorporating content from the clipboard
  347.   // Embedding a part from the clipboard
  348.   // Use a clone kind of kODCloneDropMove instead of kODClonePaste.
  349.   ......
  350.   dropResult = kODDropMove;
  351.  }
  352.  else if (dragAttributes & kODDropIsCopy) {
  353.   // This is a copy
  354.   // Refer to the following Clipboard recipes:
  355.   // Incorporating content from the clipboard
  356.   // Embedding a part from the clipboard
  357.   // Use a clone kind of kODCloneDropCopy instead of kODClonePaste.
  358.   ......
  359.   dropResult = kODDropCopy;
  360.  }
  361.  else if (dragAttributes & kODDropIsPasteAs) {
  362.   // Refer to the following Clipboard recipe:
  363.   // Embedding a Part via the Paste As Dialog
  364.   .......
  365.  }
  366.  
  367.  // For each frame embedded during the drop, remember its current in-limbo status
  368.  // before setting the status to false (not in-limbo)
  369.  wasInLimbo = frame->IsInLimbo(ev);    // for use during undo
  370.  frame->SetInLimbo(ev, kODFalse);
  371.  
  372.  return dropResult;
  373. }
  374.  
  375. Move, Copy, Paste As:
  376.  
  377. The destination part can also override a move and make the drop into a copy. However, changing a copy to a move is not allowed.
  378.  
  379. Returning the Drop Result:
  380.  
  381. After the destination part has responded to the drop, it needs to notify the source part whether it has accepted the drop as a move or a copy. The following are the predefined values of ODDropResult:
  382.  
  383.  kODDropFail: The drop has failed.
  384.  kODDropCopy: The drop is a copy.
  385.  kODDropMove: The drop is a move.
  386.  kODDropUnfinished: The drop is not finished. This value should never be returned by the destination part. It is only use by OpenDoc when DragAndDrop::StartDrag returns immediately in the case of an asynchronous drag.
  387.  
  388. This result is returned to the source part (via the system) whether the drop is accepted and what action the source part should take.
  389.  
  390.  
  391. Incorporating data from a non-OpenDoc document
  392.  
  393. When a Part's Drop method is called, it is given an ODDragItemIterator. ODDragItemIterator allows the user to access a collection of Drag Items.  As described above, it is the receiver's (or the destination part's) responsibility to iterate through all the Drag Items to find out whether it can receive the Drop. 
  394.  
  395. If the Drop comes from an OpenDoc part, there is only one Drag Item in the collection. If the Drop comes from a non-OpenDoc application (e.g., the Finder), there may be one or more Drag Item. If there is more than one Drag Item, the destination part can only accept the drop if it can accept all the drag items.
  396.  
  397. If the Drop is initiated in the Finder and the dragged object is a file, the Storage Unit of the Drag Item corresponding to the dragged object will contain a special value type in its Contents Property. The value type is "Apple:OSType:FileType:" followed by the actual file type of the file. For example, if the user drags a 'TEXT' file into a Part, "Apple:OSType:FileType:TEXT" will appear in the Contents Property of the Storage Unit of the Drag Item.
  398.  
  399. In order to access the file, the Part needs to get the HFSFlavor from the value of type "Apple:OSType:FileType:hfs " in the Contents Property. The struct is defined in Drag.h of the Macintosh Drag Manager as follows:
  400.  
  401. struct HFSFlavor {
  402.  OSType fileType;
  403.  OSType fileCreator;
  404.  unsigned short fdFlags;
  405.  FSSpec fileSpec;
  406. };
  407. typedef strcut HFSFlavor HFSFlavor;
  408.  
  409. Example:
  410.  
  411. const ODValueType kTEXTFileType = "Apple:OSType:FileType:TEXT";
  412. const ODValueType kHFSFlavorType = "Apple:OSType:Scrap:hfs ";
  413.  
  414. ODDropResult MyPartDrop(MyPart* somSelf,
  415.  Environment* ev,
  416.  ODDragItemIterator* dropInfo,
  417.  ODFacet* facet,
  418.  ODPoint* where)
  419. {
  420.  ......
  421.  for (dropSU = dropInfo->First(ev);  dropInfo->IsNotComplete(ev); dropSU = dropInfo->Next(ev))
  422.  {
  423.    // Get the HFS flavor.
  424.    dropSU->Focus(ev, kODPropContents, kODPosUndefined, kHFSFlavorType, 0, kODPosUndefined);  
  425.    ODByteArray ba;
  426.    dropSU->GetValue(ev, sizeof(HFSFlavor), &ba);
  427.  
  428.    // Use the File Spec in HFS flavor to open the file.
  429.    short fileRefNum;
  430.    OSErr err = FSpOpenDF(&(((HFSFlavor*) ba._buffer)->fileSpec), fsRdPerm, &fileRefNum);
  431.  
  432.    // You can do whatever you want to the file e.g., incorporating the data into your internal data structure.
  433.    ......
  434.  
  435.    // Cleanup
  436.    ODDisposePtr(ba._buffer);
  437.    FSClose(fileRefNum);
  438.   }
  439.   ......
  440.  
  441.  
  442. Embedding data from a non-OpenDoc document
  443.  
  444. Even when the destination part cannot incorporate the data, there may be other parts on the system which can. Following the OpenDoc model, the destination part can then embed the non-OpenDoc file and let another Part Editor handle the data.
  445.  
  446. From the destination's point of view, the recipe for embedding data from a non-OpenDoc documentan OpenDoc part is no different from embedding  an OpenDoc part. It first clones the Content Storage Unit and then it calls GetPart using the cloned Storage Unit. The OpenDoc Binding mechanism will use the corresponding Part Editor to manipulate the content of the cloned Storage Unit.
  447.  
  448. Here's what the destination part should do in its Drop method:
  449.  
  450. ......
  451.  
  452. ODDraft* dadDraft = dropSU->GetDraft(ev);
  453. ODDraft* myDraft = somSelf->GetStorageUnit(ev)->GetDraft(ev);
  454. ODDraftKey key = 0;
  455. ODID newPartID = kODNULLID;
  456. ODPart* newPart = kODNULL;
  457.  
  458. ODVolatile(key);
  459. ODVolatile(newPartID);
  460.  
  461. SOM_TRY
  462.  
  463.  // Begin a transfer from the drag-and-drop container into this draft.
  464.  key = dadDraft->BeginClone(ev, myDraft, facet->GetFrame(ev), kODClonePaste);
  465.  
  466.  // Clone the Storage Unit (which contains information on the
  467.  // non-OpenDoc document) from the drag-and-drop container.
  468.  //   Since we are cloning only a Storage Unit, there is no scoping. 0
  469.  //   is passed in to indicate that.
  470.  newPartID = dadDraft->Clone(ev, key, dropSU->GetID(ev), 0, 0);
  471.  
  472.  dadDraft->EndClone(ev, key);
  473.  
  474. SOM_CATCH_ALL
  475.  
  476.  if ( key != 0 )
  477.   dadDraft->AbortClone(ev, key);
  478.  
  479.  newPartID = kODNULLID;
  480.  
  481. SOM_ENDTRY
  482.  
  483. // Get the new embedded part
  484.  if (newPartID == kODNULLID) {
  485.   // cleanup and return error
  486.  }
  487.  else {
  488.   newPart = myDraft->AcquirePart(ev, newPartID);
  489.  }
  490.  
  491. ......
  492.  
  493. Here's what the newly created part should do in its InitPartFromStorage method:
  494.  
  495.  ......
  496.  
  497.  // myStorageUnit is a parameter to InitPartFromStorage.
  498.  if (myStorageUnit->Exists(ev, kODPropContents, kTEXTFileType, 0))
  499.  {
  500.   // Get the HFS flavor.
  501.   myStorageUnit->Focus(ev, kODPropContents, kODPosUndefined, kHFSFlavorType, 0, kODPosUndefined);  
  502.   HFSFlavor hfsFlavor;
  503.   ODByteArray ba;
  504.   myStorageUnit->GetValue(ev, sizeof(HFSFlavor), &ba);
  505.  
  506.   // Use the File Spec in HFS flavor to open the file.
  507.   short fileRefNum;
  508.   OSErr err = FSpOpenDF(&(((HFSFlavor*) ba._buffer)->fileSpec), fsRdPerm, &fileRefNum);
  509.  
  510.   // Read in the data (and possibly resources) from the file.
  511.   ......
  512.  
  513.   // Cleanup
  514.   ODDisposePtr(ba._buffer);
  515.   FSClose(fileRefNum);
  516.  
  517.   // A part should have a way to check that the storage unit contains 
  518.   // the appropriate properties and kinds. In our case, we will simply
  519.   // call our routine CheckPropertiesAndValues. This routine will
  520.   // probably remove the undesired values and add the primary value 
  521.   // kind to myStorageUnit.
  522.   CheckPropertiesAndValues(ev, myStorageUnit);
  523.  }
  524.  
  525.  ......
  526.  
  527.  
  528.  
  529.